#include "GVoxel.h"
#include <stdlib.h>	// for malloc/free
#include <string.h>	// for memcpy

// ------------------------------------------------------------------------------------------ GLAYER_DATA read/write
//
uint32_t gvfileReadVoxelLayerData( _GVOXEL_LAYER_DATA** out_DefinitionList, uint32_t nDefinitionCount, FILE* in_fp )
{
	if( 0 == in_fp || 0 == out_DefinitionList )
	{
		printf( "gvfileReadLayerData() received wrong parameters:\n"
			"_GVOXEL_LAYER_DATA** out_DefinitionList	: 0x%p"
			"uint32_t nDefinitionCount					: %i"
			"FILE* in_fp								: 0x%p"
			, out_DefinitionList
			, (int32_t)nDefinitionCount
			, in_fp 
			);
		return 0;
	}

	uint32_t size1, count1, count2, i, j;
	uint8_t bSaved;
	uint32_t product;

	_GVOXEL_LAYER_DATA* pNewData, *pOldData;

	for( i=0; i< nDefinitionCount; i++ )
	{
		if( fread( &bSaved, sizeof( bSaved ), 1, in_fp ) < 1 )
		{
			printf( "Failed to read file.\n" );
			return i;
		}
		if( 0 == bSaved )
		{
			gvfreeLayerData( &out_DefinitionList[i] ); // clear output
			continue; // null layer, continue!
		}

		
		// Create new alloc to store data
		gvcreateLayerData( &pNewData );
		if( 0 == pNewData )
		{
			printf("malloc() FAILED!! Out of memory?\n");
			return i;
		}

		size1	= 1;
		count1	= sizeof( _GVOXEL_LAYER_DATA )-sizeof( _GVOXEL_STATE_DATA** )-sizeof( void* )-sizeof( pNewData->nRefCount );
		if( fread( &pNewData->nLayerID, size1, count1, in_fp ) < (size_t)count1 )
		{
			gvfreeLayerData( &pNewData );
			printf( "Failed to read region data from file.\n" );
			return i;
		}
		if( fread( &bSaved, sizeof( bSaved ), 1, in_fp ) < 1 )
		{
			gvfreeLayerData( &pNewData );
			printf( "Failed to read file.\n" );
			return i;
		}
		if( bSaved )
		{
			count1 = GVOXEL_LAYER_WIDTH*GVOXEL_LAYER_DEPTH;
			size1	= sizeof( _GVOXEL_STATE_DATA* );
			product = size1*count1;
			_GVOXEL_STATE_DATA** buffer = (_GVOXEL_STATE_DATA**)malloc( product );
			if( 0 == buffer )
			{
				printf("malloc() FAILED!! Out of memory?\n");
				gvfreeLayerData( &pNewData );
				return i;
			}
			memset( &buffer[0], 0, product );
			if( (count2 = gvfileReadVoxelStateData( buffer, count1, in_fp )) < (size_t)count1 )
			{
				printf( "Failed to read chunk list from file.\n" );
				for( j=0; j<count2; j++ )
					gvfreeStateData( &pNewData->lstStateData[j] );
				free( buffer );
//				pNewData->lstStateData = 0;
				gvfreeLayerData( &pNewData );
				return i;
			}
			pNewData->lstStateData = buffer;
		}
		//else should be 0 already
		//{
		//	pNewData->lstStateData = 0;
		//}

		if( count1 = pNewData->nExtendedDataSize )
		{
			size1	= sizeof( char );
			void* buffer2 = malloc( size1*count1 );
			if( 0 == buffer2 )
			{
				printf("malloc() FAILED!! Out of memory?\n");
				gvfreeLayerData( &pNewData );
				return i;
			}
			if( fread( buffer2, size1, count1, in_fp ) < (size_t)count1 )
			{
				printf("Error reading from file.\n");
				free( buffer2 );
//				pNewData->pExtendedData = 0;
				gvfreeLayerData( &pNewData );
				return i;
			}
			pNewData->pExtendedData = buffer2;
		}
		//else
		//{
		//	pNewData->pExtendedData = 0;
		//}

		pOldData = out_DefinitionList[i];
		out_DefinitionList[i] = pNewData;
		gvfreeLayerData( &pOldData );		
	};

	return i;
};

uint32_t gvmemReadVoxelLayerData( _GVOXEL_LAYER_DATA** out_DefinitionList, uint32_t nDefinitionCount, const void* in_pMemoryBuffer )
{
	if( 0 == in_pMemoryBuffer || 0 == out_DefinitionList )
	{
		printf( "gvmemReadLayerData() received wrong parameters:\n"
			"_GVOXEL_LAYER_DATA** out_DefinitionList	: 0x%p"
			"uint32_t nDefinitionCount					: %i"
			"void* in_pMemoryBuffer						: 0x%p"
			, out_DefinitionList
			, (int32_t)nDefinitionCount
			, in_pMemoryBuffer
			);
		return 0;
	}


	uint32_t size1, count1, i, product;
	uint8_t bSaved;
	uint32_t byteIndex=0;

	_GVOXEL_LAYER_DATA* pNewData, *pOldData;

	for( i=0; i< nDefinitionCount; i++ )
	{
		size1	= sizeof(bSaved);
		count1	= 1;
		product = size1*count1;
		memcpy( &bSaved, &((char*)in_pMemoryBuffer)[byteIndex], product ); // read boolean telling if there's a region or not
		byteIndex += product;

		if( 0 == bSaved )
		{
			gvfreeLayerData( &out_DefinitionList[i] ); // clear output
			continue; // null layer, continue!
		}

		
		// Create new alloc to store data
		gvcreateLayerData( &pNewData );
		if( 0 == pNewData )
		{
			printf("malloc() FAILED!! Out of memory?\n");
			return byteIndex;
		}

		size1	= 1;
		count1	= sizeof( _GVOXEL_LAYER_DATA )-sizeof( _GVOXEL_STATE_DATA** )-sizeof( void* )-sizeof( pNewData->nRefCount );
		product = size1*count1;
		memcpy( &pNewData->nLayerID, &((char*)in_pMemoryBuffer)[byteIndex], product );
		byteIndex += product;

		size1	= sizeof(bSaved);
		count1	= 1;
		product = size1*count1;
		memcpy( &bSaved, &((char*)in_pMemoryBuffer)[byteIndex], product );
		byteIndex += product;

		if( bSaved )
		{
			size1	= sizeof( _GVOXEL_STATE_DATA* );
			count1	= GVOXEL_LAYER_WIDTH*GVOXEL_LAYER_DEPTH;
			product = size1*count1;
			_GVOXEL_STATE_DATA** buffer = (_GVOXEL_STATE_DATA**)malloc( product );
			if( 0 == buffer )
			{
				printf("malloc() FAILED!! Out of memory?\n");
				gvfreeLayerData( &pNewData );
				return i;
			}
			memset( &buffer[0], 0, product );
			byteIndex += gvmemReadVoxelStateData( buffer, count1, &((char*)in_pMemoryBuffer)[byteIndex] );
			pNewData->lstStateData = buffer;
		}
		//else should be 0 already
		//{
		//	pNewData->lstStateData = 0;
		//}

		if( count1 = pNewData->nExtendedDataSize )
		{
			size1	= sizeof( char );
			product = size1*count1;
			void* buffer2 = malloc( size1*count1 );
			if( 0 == buffer2 )
			{
				printf("malloc() FAILED!! Out of memory?\n");
				gvfreeLayerData( &pNewData );
				return i;
			}
			memcpy( buffer2, &((char*)in_pMemoryBuffer)[byteIndex], product );
			pNewData->pExtendedData = buffer2;
			byteIndex += product;
		}
		//else
		//{
		//	pNewData->pExtendedData = 0;
		//}

		pOldData = out_DefinitionList[i];
		out_DefinitionList[i] = pNewData;
		gvfreeLayerData( &pOldData );		
	};

	return byteIndex;
};

uint32_t gvfileWriteVoxelLayerData( _GVOXEL_LAYER_DATA** in_DefinitionList, uint32_t nDefinitionCount, FILE* out_fp )
{
	if( 0 == out_fp || 0 == in_DefinitionList )
	{
		printf( "gvfileWriteLayerData() received wrong parameters:\n"
			"_GVOXEL_LAYER_DATA** in_DefinitionList		: 0x%p"
			"uint32_t nDefinitionCount					: %i"
			"void* out_fp								: 0x%p"
			, in_DefinitionList
			, (int32_t)nDefinitionCount
			, out_fp
			);
		return 0;
	}

	uint32_t size1, count1, i;
	uint8_t bSaved;
	for( i=0; i< nDefinitionCount; i++ )
	{
		bSaved = ( (0 == in_DefinitionList[i]) ? 0 : 1 );
		size1	= sizeof( bSaved );
		count1	= 1;
		if( fwrite( &bSaved, size1, count1, out_fp ) < (size_t)count1 )
		{
			printf("Failed to write region data to file.\n");
			return i;
		}
		if( 0 == bSaved )
			continue;

		size1	= 1;
		count1	= sizeof( _GVOXEL_LAYER_DATA )-sizeof( _GVOXEL_STATE_DATA** )-sizeof( void* )-sizeof( in_DefinitionList[i]->nRefCount );
		if( fwrite( &in_DefinitionList[i]->nLayerID, size1, count1, out_fp ) < (size_t)count1 )
		{
			printf("Failed to write region data to file.\n");
			return i;
		}

		bSaved = ( (0 == in_DefinitionList[i]->lstStateData) ? 0 : 1 );
		size1	= sizeof( bSaved );
		count1	= 1;
		if( fwrite( &bSaved, size1, count1, out_fp ) < (size_t)count1 )
		{
			printf("Failed to write region data to file.\n");
			return i;
		}
		if( bSaved )
		{
			count1 = GVOXEL_LAYER_WIDTH*GVOXEL_LAYER_DEPTH;
			if( gvfileWriteVoxelStateData( in_DefinitionList[i]->lstStateData, count1, out_fp ) < count1 )
			{
				printf("gvfileWriteVoxelStateData() FAILED!\n");
				return i;
			}
		}
		if( count1 = in_DefinitionList[i]->nExtendedDataSize )
		{
			size1	= sizeof( char );
			if( fwrite( in_DefinitionList[i]->pExtendedData, size1, count1, out_fp ) < (size_t)count1 )
			{
				printf("gvfileWriteVoxelStateData() FAILED!\n");
				return i;
			}
		}
	};
	return i;
};

uint32_t gvmemWriteVoxelLayerData( _GVOXEL_LAYER_DATA** in_DefinitionList, uint32_t nDefinitionCount, void* out_pMemoryBuffer )
{
	if( 0 == in_DefinitionList || 0 == nDefinitionCount )
	{
		printf( "gvmemWriteLayerData() received wrong parameters:\n"
			"_GVOXEL_LAYER_DATA** in_DefinitionList		: 0x%p"
			"uint32_t nDefinitionCount					: %i"
			"void* out_pMemoryBuffer					: 0x%p"
			, in_DefinitionList
			, (int32_t)nDefinitionCount
			, out_pMemoryBuffer
			);
		return 0;
	}

	uint32_t size1, count1, i, product;
	uint8_t bSaved;
	uint32_t byteIndex=0;
	for( i=0; i< nDefinitionCount; i++ )
	{
		// save a 32 bit boolean telling if the region is gonna be saved to the file (0 for a null region)
		bSaved = ( (0 == in_DefinitionList[i]) ? 0 : 1 );
		size1	= sizeof( bSaved );
		count1	= 1;
		product	= size1*count1;
		if( out_pMemoryBuffer )
			memcpy( &((char*)out_pMemoryBuffer)[byteIndex], &bSaved, product );
		byteIndex += product;
		if( 0 == bSaved )
			continue;
		
		size1	= 1;
		count1	= sizeof( _GVOXEL_LAYER_DATA )-sizeof( _GVOXEL_STATE_DATA** )-sizeof( void* )-sizeof( in_DefinitionList[i]->nRefCount );
		product	= size1*count1;
		if( out_pMemoryBuffer )
			memcpy( &((char*)out_pMemoryBuffer)[byteIndex], &(in_DefinitionList[i]->nLayerID), product );
		byteIndex += product;

		bSaved = ( (0 == in_DefinitionList[i]->lstStateData) ? 0 : 1 );
		size1	= sizeof( bSaved );
		count1	= 1;
		product	= size1*count1;
		if( out_pMemoryBuffer )
			memcpy( &((char*)out_pMemoryBuffer)[byteIndex], &bSaved, product );
		byteIndex += product;

		if( bSaved )
		{
			count1 = GVOXEL_LAYER_WIDTH*GVOXEL_LAYER_DEPTH;
			if( out_pMemoryBuffer )
				byteIndex += gvmemWriteVoxelStateData( &in_DefinitionList[i]->lstStateData[0], count1, &((char*)out_pMemoryBuffer)[byteIndex] ); 
			else
				byteIndex += gvmemWriteVoxelStateData( &in_DefinitionList[i]->lstStateData[0], count1, 0 ); 
		}
		if( count1 = in_DefinitionList[i]->nExtendedDataSize )
		{
			size1	= sizeof( char );
			product	= size1*count1;
			if( out_pMemoryBuffer )
				memcpy( &((char*)out_pMemoryBuffer)[byteIndex], in_DefinitionList[i]->pExtendedData, product );
			byteIndex += product;
		}
	};

	return byteIndex;
};
